<FrameworkSwitchCourse {fw} />

# Byte-Pair Encoding Tokenization[[byte-pair-encoding-tokenization]]

<CourseFloatingBanner chapter={6}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter6/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section5.ipynb"},
]} />

Byte-Pair Encoding (BPE) ကို မူလက texts တွေကို compress လုပ်ဖို့ algorithm အဖြစ် တီထွင်ခဲ့ပြီး၊ နောက်ပိုင်းမှာ GPT model ကို pretraining လုပ်တဲ့အခါ OpenAI က tokenization အတွက် အသုံးပြုခဲ့ပါတယ်။ ဒါကို GPT, GPT-2, RoBERTa, BART, နဲ့ DeBERTa အပါအဝင် Transformer models အများအပြားက အသုံးပြုကြပါတယ်။

<Youtube id="HEikzVL-lZU"/>

> [!TIP]
> 💡 ဒီအပိုင်းက BPE ကို အသေးစိတ်ဖော်ပြပြီး၊ အပြည့်အစုံ implementation ကိုပါ ပြသထားပါတယ်။ tokenization algorithm ရဲ့ အထွေထွေ overview ကိုပဲ လိုချင်တယ်ဆိုရင် နောက်ဆုံးအပိုင်းကို ကျော်သွားနိုင်ပါတယ်။

## Training Algorithm[[training-algorithm]]

BPE training က corpus မှာ အသုံးပြုထားတဲ့ ထူးခြားတဲ့ words တွေကို (normalization နဲ့ pre-tokenization အဆင့်တွေ ပြီးစီးပြီးနောက်) တွက်ချက်ခြင်းဖြင့် စတင်ပါတယ်။ ထို့နောက် အဲဒီ words တွေကို ရေးသားဖို့ အသုံးပြုထားတဲ့ symbols အားလုံးကို ယူပြီး vocabulary ကို တည်ဆောက်ပါတယ်။ ရိုးရှင်းတဲ့ ဥပမာတစ်ခုအနေနဲ့၊ ကျွန်တော်တို့ရဲ့ corpus က အောက်ပါ words ငါးလုံးကို အသုံးပြုတယ်လို့ ဆိုကြပါစို့။

```
"hug", "pug", "pun", "bun", "hugs"
```

base vocabulary ကတော့ `["b", "g", "h", "n", "p", "s", "u"]` ဖြစ်ပါလိမ့်မယ်။ လက်တွေ့ကမ္ဘာ ကိစ္စတွေအတွက်၊ အဲဒီ base vocabulary မှာ အနည်းဆုံး ASCII characters အားလုံးနဲ့ Unicode characters အချို့လည်း ပါဝင်ပါလိမ့်မယ်။ သင် tokenize လုပ်နေတဲ့ ဥပမာတစ်ခုက training corpus မှာ မပါဝင်တဲ့ character တစ်ခုကို အသုံးပြုတယ်ဆိုရင်၊ အဲဒီ character ကို unknown token အဖြစ် ပြောင်းလဲပါလိမ့်မယ်။ ဒါက NLP models အများအပြားက emojis ပါဝင်တဲ့ content တွေကို analyze လုပ်ရာမှာ အလွန်ညံ့ဖျင်းရခြင်းရဲ့ အကြောင်းရင်းတစ်ခုပါပဲ။

> [!TIP]
> GPT-2 နဲ့ RoBERTa tokenizers တွေ (တော်တော်လေး ဆင်တူပါတယ်) က ဒါကို ဖြေရှင်းဖို့ လိမ္မာပါးနပ်တဲ့ နည်းလမ်းတစ်ခုရှိပါတယ်- ၎င်းတို့ဟာ words တွေကို Unicode characters တွေနဲ့ ရေးထားတယ်လို့ မယူဆဘဲ bytes တွေနဲ့ ရေးထားတယ်လို့ ယူဆပါတယ်။ ဒီနည်းနဲ့ base vocabulary က small size (၂၅၆) ရှိပေမယ့်၊ သင်စဉ်းစားနိုင်တဲ့ character တိုင်း ပါဝင်နေမှာဖြစ်ပြီး unknown token အဖြစ် ပြောင်းလဲခံရမှာ မဟုတ်ပါဘူး။ ဒီနည်းလမ်းကို *byte-level BPE* လို့ ခေါ်ပါတယ်။

ဒီ base vocabulary ရရှိပြီးနောက်၊ လက်ရှိ vocabulary ထဲက elements နှစ်ခုကို အတူတကွ ပေါင်းစပ်ဖို့ စည်းမျဉ်းတွေဖြစ်တဲ့ *merges* တွေကို သင်ယူခြင်းဖြင့် လိုချင်တဲ့ vocabulary size ရောက်တဲ့အထိ tokens အသစ်တွေ ထပ်ထည့်ပါတယ်။ ဒါကြောင့်၊ အစပိုင်းမှာ ဒီ merges တွေက characters နှစ်ခုပါတဲ့ tokens တွေကို ဖန်တီးပါလိမ့်မယ်၊ ပြီးတော့ training တိုးတက်လာတာနဲ့အမျှ ပိုရှည်တဲ့ subwords တွေကို ဖန်တီးပါလိမ့်မယ်။

tokenizer training လုပ်နေစဉ် မည်သည့်အဆင့်မှာမဆို၊ BPE algorithm က လက်ရှိ tokens တွေထဲက အများဆုံး ဖြစ်လေ့ရှိတဲ့ pair (ဒီနေရာမှာ "pair" ဆိုတာ word တစ်ခုထဲက ဆက်တိုက် tokens နှစ်ခုကို ဆိုလိုပါတယ်) ကို ရှာဖွေပါလိမ့်မယ်။ အဲဒီ အများဆုံး ဖြစ်လေ့ရှိတဲ့ pair က merge လုပ်ခံရမှာဖြစ်ပြီး၊ နောက်တစ်ဆင့်အတွက် ထပ်ခါတလဲလဲ လုပ်ဆောင်သွားမှာပါ။

ကျွန်တော်တို့ ယခင်ဥပမာကို ပြန်သွားရအောင်၊ words တွေမှာ အောက်ပါ frequencies တွေ ရှိတယ်လို့ ယူဆကြပါစို့။

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

ဆိုလိုတာက `"hug"` က corpus မှာ ၁၀ ကြိမ်၊ `"pug"` က ၅ ကြိမ်၊ `"pun"` က ၁၂ ကြိမ်၊ `"bun"` က ၄ ကြိမ်၊ နဲ့ `"hugs"` က ၅ ကြိမ် ပါဝင်ပါတယ်။ ကျွန်တော်တို့ training ကို word တစ်ခုစီကို characters တွေအဖြစ် ပိုင်းခြားခြင်း (ကျွန်တော်တို့ရဲ့ initial vocabulary ကို ဖွဲ့စည်းတဲ့ characters တွေ) ဖြင့် စတင်ပါတယ်၊ ဒါကြောင့် word တစ်ခုစီကို tokens list တစ်ခုအဖြစ် မြင်နိုင်ပါတယ်။

```
("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
```

ပြီးရင် pairs တွေကို ကြည့်ပါတယ်။ pair `("h", "u")` က words `"hug"` နဲ့ `"hugs"` မှာ ပါဝင်တာကြောင့် corpus မှာ စုစုပေါင်း ၁၅ ကြိမ် ရှိပါတယ်။ ဒါပေမယ့် အများဆုံး ဖြစ်လေ့ရှိတဲ့ pair တော့ မဟုတ်ပါဘူး။ အဲဒါက `("u", "g")` အတွက် ဖြစ်ပြီး၊ ဒါက `"hug"`, `"pug"`, နဲ့ `"hugs"` မှာ ပါဝင်တာကြောင့် vocabulary မှာ စုစုပေါင်း ၂၀ ကြိမ် ရှိပါတယ်။

ဒါကြောင့်၊ tokenizer က သင်ယူတဲ့ ပထမဆုံး merge rule က `("u", "g") -> "ug"` ဖြစ်ပြီး၊ ဒါက `"ug"` ကို vocabulary ထဲကို ထည့်သွင်းမှာဖြစ်ပြီး၊ အဲဒီ pair ကို corpus ထဲက words အားလုံးမှာ merge လုပ်သင့်တယ်လို့ ဆိုလိုပါတယ်။ ဒီအဆင့်ရဲ့ အဆုံးမှာ၊ vocabulary နဲ့ corpus က ဒီလိုဖြစ်လာပါလိမ့်မယ်။

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
```

အခု ကျွန်တော်တို့မှာ characters နှစ်ခုထက် ပိုရှည်တဲ့ token တစ်ခုကို ဖြစ်ပေါ်စေတဲ့ pairs အချို့ရှိပါတယ်။ ဥပမာ `("h", "ug")` pair (corpus မှာ ၁၅ ကြိမ် ပါဝင်ပါတယ်။)။ ဒီအဆင့်မှာ အများဆုံး ဖြစ်လေ့ရှိတဲ့ pair က `("u", "n")` ဖြစ်ပြီး၊ corpus မှာ ၁၆ ကြိမ် ပါဝင်တာကြောင့်၊ သင်ယူတဲ့ ဒုတိယ merge rule က `("u", "n") -> "un"` ဖြစ်ပါတယ်။ ဒါကို vocabulary ထဲကို ထည့်သွင်းပြီး လက်ရှိ occurrences အားလုံးကို merge လုပ်ခြင်းက ကျွန်တော်တို့ကို အောက်ပါအတိုင်း ဖြစ်စေပါတယ်။

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)
```

အခု အများဆုံး ဖြစ်လေ့ရှိတဲ့ pair က `("h", "ug")` ဖြစ်တာကြောင့်၊ ကျွန်တော်တို့ merge rule `("h", "ug") -> "hug"` ကို သင်ယူပြီး၊ ကျွန်တော်တို့ရဲ့ ပထမဆုံး သုံးလုံးပါ token ကို ရရှိပါတယ်။ merge လုပ်ပြီးနောက်၊ corpus က ဒီလိုဖြစ်လာပါလိမ့်မယ်။

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
```

ပြီးတော့ လိုချင်တဲ့ vocabulary size ရောက်တဲ့အထိ ဒီလိုပဲ ဆက်လုပ်သွားမှာပါ။

> [!TIP]
> ✏️ **အခု သင့်အလှည့်!** နောက်ထပ် merge rule က ဘာဖြစ်မယ်လို့ သင်ထင်သလဲ။

## Tokenization Algorithm[[tokenization-algorithm]]

Tokenization က training လုပ်ငန်းစဉ်ကို နီးနီးကပ်ကပ် လိုက်နာပါတယ်၊ ဆိုလိုတာက inputs အသစ်တွေကို အောက်ပါအဆင့်တွေကို အသုံးပြုပြီး tokenize လုပ်ပါတယ်။

၁။ Normalization
၂။ Pre-tokenization
၃။ Words တွေကို တစ်ဦးချင်း characters တွေအဖြစ် ပိုင်းခြားခြင်း
၄။ သင်ယူထားတဲ့ merge rules တွေကို အဲဒီ splits တွေပေါ်မှာ အစဉ်လိုက် အသုံးပြုခြင်း

training လုပ်နေစဉ်အတွင်း ကျွန်တော်တို့ အသုံးပြုခဲ့တဲ့ ဥပမာကို ယူကြစို့၊ သင်ယူခဲ့တဲ့ merge rules သုံးခုနဲ့အတူ...

```
("u", "g") -> "ug"
("u", "n") -> "un"
("h", "ug") -> "hug"
```

`"bug"` ဆိုတဲ့ word ကို `["b", "ug"]` အဖြစ် tokenize လုပ်ပါလိမ့်မယ်။ `"mug"` ကိုတော့ `["[UNK]", "ug"]` အဖြစ် tokenize လုပ်ပါလိမ့်မယ်။ ဘာလို့လဲဆိုတော့ "m" စာလုံးက base vocabulary မှာ မပါဝင်လို့ပါ။ အလားတူပဲ၊ `"thug"` ဆိုတဲ့ word ကို `["[UNK]", "hug"]` အဖြစ် tokenize လုပ်ပါလိမ့်မယ်။ "t" စာလုံးက base vocabulary မှာ မပါဝင်ပါဘူး၊ ပြီးတော့ merge rules တွေကို အသုံးပြုခြင်းက ပထမဆုံး "u" နဲ့ "g" ကို merge လုပ်ပြီးနောက် "h" နဲ့ "ug" ကို merge လုပ်တာကို ဖြစ်ပေါ်စေပါတယ်။

> [!TIP]
> ✏️ **အခု သင့်အလှည့်!** `"unhug"` ဆိုတဲ့ word ကို ဘယ်လို tokenize လုပ်မယ်လို့ သင်ထင်သလဲ။

## BPE ကို အကောင်အထည်ဖော်ခြင်း (Implementing BPE)[[implementing-bpe]]

အခု BPE algorithm ကို အကောင်အထည်ဖော်တာကို ကြည့်ရအောင်။ ဒါက big corpus ပေါ်မှာ တကယ်အသုံးပြုနိုင်မယ့် optimized version တော့ မဟုတ်ပါဘူး။ algorithm ကို ပိုမိုကောင်းမွန်စွာ နားလည်နိုင်ဖို့ code ကို ပြသချင်တာပါ။

ပထမဆုံး ကျွန်တော်တို့ corpus တစ်ခု လိုအပ်ပါတယ်၊ ဒါကြောင့် sentences အနည်းငယ်ပါတဲ့ ရိုးရှင်းတဲ့ corpus တစ်ခုကို ဖန်တီးကြစို့။

```python
corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
```

နောက်တစ်ဆင့်ကတော့ အဲဒီ corpus ကို words တွေအဖြစ် pre-tokenize လုပ်ဖို့ပါပဲ။ ကျွန်တော်တို့ BPE tokenizer (GPT-2 လိုမျိုး) ကို ပြန်လည်ထုတ်လုပ်နေတာကြောင့်၊ pre-tokenization အတွက် `gpt2` tokenizer ကို အသုံးပြုပါမယ်။

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("gpt2")
```

ပြီးရင် pre-tokenization လုပ်နေစဉ် corpus ထဲက word တစ်ခုစီရဲ့ frequencies တွေကို တွက်ချက်ပါတယ်။

```python
from collections import defaultdict

word_freqs = defaultdict(int)

for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

print(word_freqs)
```

```python out
defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
    'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
    'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
    'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
```

နောက်တစ်ဆင့်ကတော့ corpus မှာ အသုံးပြုထားတဲ့ characters အားလုံးနဲ့ ဖွဲ့စည်းထားတဲ့ base vocabulary ကို တွက်ချက်ဖို့ပါ။

```python
alphabet = []

for word in word_freqs.keys():
    for letter in word:
        if letter not in alphabet:
            alphabet.append(letter)
alphabet.sort()

print(alphabet)
```

```python out
[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
  't', 'u', 'v', 'w', 'y', 'z', 'Ġ']
```

အဲဒီ vocabulary ရဲ့ အစမှာ model က အသုံးပြုတဲ့ special tokens တွေကိုလည်း ထည့်သွင်းပါတယ်။ GPT-2 ရဲ့ ကိစ္စမှာ၊ တစ်ခုတည်းသော special token က `"<|endoftext|>"` ဖြစ်ပါတယ်။

```python
vocab = ["<|endoftext|>"] + alphabet.copy()
```

training စတင်နိုင်ဖို့အတွက် word တစ်ခုစီကို individual characters တွေအဖြစ် ပိုင်းခြားဖို့ အခု ကျွန်တော်တို့ လိုအပ်ပါတယ်။

```python
splits = {word: [c for c in word] for word in word_freqs.keys()}
```

training အတွက် ကျွန်တော်တို့ အဆင်သင့်ဖြစ်ပြီဆိုတော့၊ pair တစ်ခုစီရဲ့ frequency ကို တွက်ချက်တဲ့ function တစ်ခု ရေးကြရအောင်။ ဒါကို training ရဲ့ အဆင့်တိုင်းမှာ အသုံးပြုဖို့ လိုအပ်ပါလိမ့်မယ်။

```python
def compute_pair_freqs(splits):
    pair_freqs = defaultdict(int)
    for word, freq in word_freqs.items():
        split = splits[word]
        if len(split) == 1:
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            pair_freqs[pair] += freq
    return pair_freqs
```

initial splits တွေပြီးနောက် ဒီ dictionary ရဲ့ အစိတ်အပိုင်းတစ်ခုကို ကြည့်ရအောင်။

```python
pair_freqs = compute_pair_freqs(splits)

for i, key in enumerate(pair_freqs.keys()):
    print(f"{key}: {pair_freqs[key]}")
    if i >= 5:
        break
```

```python out
('T', 'h'): 3
('h', 'i'): 3
('i', 's'): 5
('Ġ', 'i'): 2
('Ġ', 't'): 7
('t', 'h'): 3
```

အခု၊ အများဆုံး ဖြစ်လေ့ရှိတဲ့ pair ကို ရှာဖွေတာက မြန်ဆန်တဲ့ loop တစ်ခုပဲ လိုပါတယ်။

```python
best_pair = ""
max_freq = None

for pair, freq in pair_freqs.items():
    if max_freq is None or max_freq < freq:
        best_pair = pair
        max_freq = freq

print(best_pair, max_freq)
```

```python out
('Ġ', 't') 7
```

ဒါကြောင့် ပထမဆုံး သင်ယူရမယ့် merge က `('Ġ', 't') -> 'Ġt'` ဖြစ်ပြီး၊ ကျွန်တော်တို့ `Ġt` ကို vocabulary ထဲကို ထည့်သွင်းပါတယ်။

```python
merges = {("Ġ", "t"): "Ġt"}
vocab.append("Ġt")
```

ဆက်လက်လုပ်ဆောင်ဖို့၊ အဲဒီ merge ကို ကျွန်တော်တို့ရဲ့ `splits` dictionary မှာ အသုံးပြုဖို့ လိုအပ်ပါတယ်။ ဒါအတွက် နောက်ထပ် function တစ်ခု ရေးကြရအောင်။

```python
def merge_pair(a, b, splits):
    for word in word_freqs:
        split = splits[word]
        if len(split) == 1:
            continue

        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                split = split[:i] + [a + b] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits
```

ပြီးတော့ ပထမဆုံး merge ရဲ့ ရလဒ်ကို ကြည့်နိုင်ပါတယ်။

```py
splits = merge_pair("Ġ", "t", splits)
print(splits["Ġtrained"])
```

```python out
['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
```

အခု ကျွန်တော်တို့ လိုချင်တဲ့ merges အားလုံး သင်ယူပြီးတဲ့အထိ loop လုပ်ဖို့ လိုအပ်တဲ့အရာအားလုံး ရှိပါပြီ။ vocabulary size ၅၀ ကို ရည်ရွယ်ကြစို့။

```python
vocab_size = 50

while len(vocab) < vocab_size:
    pair_freqs = compute_pair_freqs(splits)
    best_pair = ""
    max_freq = None
    for pair, freq in pair_freqs.items():
        if max_freq is None or max_freq < freq:
            best_pair = pair
            max_freq = freq
    splits = merge_pair(*best_pair, splits)
    merges[best_pair] = best_pair[0] + best_pair[1]
    vocab.append(best_pair[0] + best_pair[1])
```

ရလဒ်အနေနဲ့၊ ကျွန်တော်တို့ merge rules ၁၉ ခု သင်ယူခဲ့ပါပြီ (initial vocabulary မှာ size ၃၁ ရှိပါတယ်။ alphabet ထဲက characters ၃၀ နဲ့ special token)။

```py
print(merges)
```

```python out
{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
 ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
 ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
 ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
```

ပြီးတော့ vocabulary က special token၊ initial alphabet နဲ့ merges အားလုံးရဲ့ ရလဒ်တွေနဲ့ ဖွဲ့စည်းထားပါတယ်။

```py
print(vocab)
```

```python out
['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
```

> [!TIP]
> 💡 တူညီတဲ့ corpus ပေါ်မှာ `train_new_from_iterator()` ကို အသုံးပြုတာက အတိအကျတူညီတဲ့ vocabulary ကို ဖြစ်ပေါ်စေမှာ မဟုတ်ပါဘူး။ ဒါက အများဆုံး ဖြစ်လေ့ရှိတဲ့ pair ကို ရွေးချယ်ခွင့်ရှိတဲ့အခါ၊ ကျွန်တော်တို့က ပထမဆုံး တွေ့တဲ့အရာကို ရွေးချယ်ခဲ့ပြီး၊ 🤗 Tokenizers library ကတော့ ၎င်းရဲ့ inner IDs ပေါ် အခြေခံပြီး ပထမဆုံးအရာကို ရွေးချယ်လို့ပါပဲ။

text အသစ်တစ်ခုကို tokenize လုပ်ဖို့၊ ဒါကို pre-tokenize လုပ်၊ ပိုင်းခြားပြီး၊ သင်ယူထားတဲ့ merge rules အားလုံးကို အသုံးပြုပါတယ်။

```python
def tokenize(text):
    pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in pre_tokenize_result]
    splits = [[l for l in word] for word in pre_tokenized_text]
    for pair, merge in merges.items():
        for idx, split in enumerate(splits):
            i = 0
            while i < len(split) - 1:
                if split[i] == pair[0] and split[i + 1] == pair[1]:
                    split = split[:i] + [merge] + split[i + 2 :]
                else:
                    i += 1
            splits[idx] = split

    return sum(splits, [])
```

ဒါကို alphabet ထဲက characters တွေနဲ့ ဖွဲ့စည်းထားတဲ့ မည်သည့် text ပေါ်မှာမဆို စမ်းသပ်နိုင်ပါတယ်။

```py
tokenize("This is not a token.")
```

```python out
['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.']
```

> [!WARNING]
> ⚠️ ကျွန်တော်တို့ရဲ့ implementation က unknown character တွေအတွက် ဘာမှလုပ်ဆောင်ထားခြင်း မရှိတာကြောင့် unknown character ရှိရင် error ကို ပြပါလိမ့်မယ်။ GPT-2 မှာ တကယ်တော့ unknown token မရှိပါဘူး (byte-level BPE ကို အသုံးပြုတဲ့အခါ unknown character တစ်ခုရဖို့ မဖြစ်နိုင်ပါဘူး)၊ ဒါပေမယ့် ကျွန်တော်တို့ initial vocabulary မှာ ဖြစ်နိုင်တဲ့ bytes အားလုံးကို ထည့်သွင်းမထားတဲ့အတွက် ဒီနေရာမှာ ဒါက ဖြစ်နိုင်ပါတယ်။ BPE ရဲ့ ဒီကဏ္ဍက ဒီအပိုင်းရဲ့ scope ကို ကျော်လွန်နေတာကြောင့်၊ အသေးစိတ်အချက်အလက်တွေကို ကျွန်တော်တို့ ချန်လှပ်ထားခဲ့ပါတယ်။

BPE algorithm အတွက် ဒီလောက်ပါပဲ! နောက်မှာ WordPiece ကို ကြည့်ရအောင်။


## ဝေါဟာရ ရှင်းလင်းချက် (Glossary)

*   **Byte-Pair Encoding (BPE)**: စာသားများကို compress လုပ်ရန် မူလက တီထွင်ခဲ့ပြီး နောက်ပိုင်းတွင် subword tokenization အတွက် အသုံးပြုသော algorithm တစ်မျိုး။
*   **GPT (Generative Pre-trained Transformer)**: OpenAI မှ pretrain လုပ်ထားသော Transformer-based language model။
*   **RoBERTa**: BERT ကို အခြေခံထားပြီး training data ပိုမိုများပြားကာ training approach ကို ပြောင်းလဲထားသော language model။
*   **BART**: Encoder-decoder Transformer model တစ်မျိုးဖြစ်ပြီး text generation နှင့် sequence-to-sequence tasks များအတွက် အသုံးပြုသည်။
*   **DeBERTa**: Microsoft မှ တီထွင်ထားသော BERT-style model တစ်ခုဖြစ်ပြီး attention mechanism ကို တိုးတက်အောင် လုပ်ဆောင်ထားသည်။
*   **OpenAI**: Artificial Intelligence (AI) သုတေသနကုမ္ပဏီ။
*   **Tokenization**: စာသား (သို့မဟုတ် အခြားဒေတာ) ကို AI မော်ဒယ်များ စီမံဆောင်ရွက်နိုင်ရန် tokens တွေအဖြစ် ပိုင်းခြားပေးသည့် လုပ်ငန်းစဉ်။
*   **Corpus**: စာသား (သို့မဟုတ် အခြားဒေတာ) အစုအဝေးကြီးတစ်ခု။
*   **Normalization**: စာသားကို သန့်ရှင်းရေးလုပ်ခြင်း (ဥပမာ- needless whitespace ဖယ်ရှားခြင်း၊ lowercasing, accents ဖယ်ရှားခြင်း)။
*   **Pre-tokenization**: Subword tokenization မလုပ်ဆောင်မီ စာသားကို ပိုမိုသေးငယ်သော entities (ဥပမာ- words) အဖြစ် အကြိုပိုင်းခြားခြင်း။
*   **Vocabulary**: tokenizer သို့မဟုတ် model တစ်ခုက သိရှိနားလည်ပြီး ကိုင်တွယ်နိုင်သော ထူးခြားသည့် tokens များ စုစုပေါင်း။
*   **Symbols**: စာသားများတွင် အသုံးပြုသော စာလုံးများ သို့မဟုတ် သင်္ကေတများ။
*   **ASCII Characters**: English alphabet, numbers, နှင့် basic symbols များ ပါဝင်သော character encoding standard။
*   **Unicode Characters**: ကမ္ဘာပေါ်ရှိ ဘာသာစကားအားလုံးနီးပါးကို ကိုယ်စားပြုနိုင်သော character encoding standard။
*   **Unknown Token (UNK)**: vocabulary တွင် မပါဝင်သော character သို့မဟုတ် word ကို ကိုယ်စားပြုသော token။
*   **Byte-level BPE**: GPT-2 နှင့် RoBERTa ကဲ့သို့သော tokenizers များတွင် အသုံးပြုသော BPE ၏ အမျိုးအစားတစ်ခုဖြစ်ပြီး Unicode characters များကို bytes များအဖြစ် ပြောင်းလဲပြီး BPE ကို bytes များပေါ်တွင် လုပ်ဆောင်သည်။ ၎င်းသည် unknown token ပြဿနာကို ရှောင်ရှားသည်။
*   **Merges**: BPE algorithm တွင် tokens နှစ်ခုကို ပေါင်းစပ်ရန် သင်ယူထားသော စည်းမျဉ်းများ။
*   **Consecutive Tokens**: တစ်ခုနှင့်တစ်ခု ဆက်တိုက် ဖြစ်ပေါ်နေသော tokens များ။
*   **Frequency**: အကြိမ်ရေ၊ မည်မျှမကြာခဏ ဖြစ်ပေါ်သည်ကို ဖော်ပြခြင်း။
*   **Initial Vocabulary (Base Vocabulary)**: BPE algorithm စတင်သည့်အခါ အသုံးပြုသော အခြေခံ tokens များ။
*   **`AutoTokenizer`**: Hugging Face Transformers library မှာ ပါဝင်တဲ့ class တစ်ခုဖြစ်ပြီး မော်ဒယ်အမည်ကို အသုံးပြုပြီး သက်ဆိုင်ရာ tokenizer ကို အလိုအလျောက် load လုပ်ပေးသည်။
*   **`gpt2` (Checkpoint Identifier)**: GPT-2 model အတွက် အသုံးပြုသော identifier။
*   **`backend_tokenizer`**: 🤗 Transformers `tokenizer` object မှ underlying tokenizer (🤗 Tokenizers library က) ကို ဝင်ရောက်ကြည့်ရှုနိုင်စေသော attribute။
*   **`pre_tokenize_str()` Method**: Pre-tokenizer object မှ string တစ်ခုကို pre-tokenize လုပ်သော method။
*   **`defaultdict(int)`**: Python ၏ `collections` module မှ dictionary subclass တစ်ခုဖြစ်ပြီး key တစ်ခု မရှိပါက default value (ဒီနေရာတွင် `0`) ကို ပေးသည်။
*   **`word_freqs`**: Corpus အတွင်းရှိ word တစ်ခုစီ၏ frequency ကို သိမ်းဆည်းထားသော dictionary။
*   **`alphabet`**: Corpus အတွင်းရှိ ထူးခြားသော characters များအားလုံး၏ list။
*   **Special Tokens**: Tokenizer သို့မဟုတ် model အတွက် သီးခြားအဓိပ္ပာယ်ရှိသော tokens များ (ဥပမာ- `"<|endoftext|>"`, `[CLS]`, `[SEP]`, `[PAD]`)။
*   **`"<|endoftext|>"`**: GPT-2 tokenizer တွင် အသုံးပြုသော special token။
*   **`vocab`**: BPE training လုပ်ငန်းစဉ်အတွင်း တိုးချဲ့သွားမည့် vocabulary list။
*   **`splits`**: word တစ်ခုစီကို characters များအဖြစ် ပိုင်းခြားထားသော dictionary။
*   **`compute_pair_freqs()` Function**: `splits` dictionary ပေါ်တွင် အခြေခံ၍ tokens pairs များ၏ frequencies များကို တွက်ချက်သော function။
*   **`pair_freqs`**: tokens pairs များနှင့် ၎င်းတို့၏ frequencies များကို သိမ်းဆည်းထားသော dictionary။
*   **`best_pair`**: အများဆုံး ဖြစ်လေ့ရှိသော tokens pair။
*   **`max_freq`**: အများဆုံး ဖြစ်လေ့ရှိသော pair ၏ frequency။
*   **`merges`**: သင်ယူထားသော merge rules များကို သိမ်းဆည်းထားသော dictionary။
*   **`vocab_size`**: လိုချင်သော vocabulary ၏ အမြင့်ဆုံး အရွယ်အစား။
*   **`merge_pair()` Function**: `splits` dictionary ထဲတွင် သတ်မှတ်ထားသော tokens pair ကို merge လုပ်သော function။
*   **`tokenize()` Function**: BPE algorithm ကို အသုံးပြု၍ text အသစ်တစ်ခုကို tokenize လုပ်သော function။
*   **`_tokenizer.pre_tokenizer.pre_tokenize_str(text)`**: underlying pre-tokenizer ကို အသုံးပြု၍ text ကို pre-tokenize လုပ်ခြင်း။
*   **`sum(splits, [])`**: nested list များကို single, flat list အဖြစ် ပေါင်းစပ်သော Python idiom။